#-*- coding: utf-8 -*-

import sys
import re
import urllib
import hashlib
import datetime
from config import host_name
from data import sync_db, create_db_connection


def password_hash(text):
	return hashlib.md5(text).hexdigest()


def get_icon_url(email):
	icon_size = 40
	gravatar_url = "http://www.gravatar.com/avatar/" + hashlib.md5(email.lower()).hexdigest() + "?"
	gravatar_url += urllib.urlencode({'d':'retro', 's':str(icon_size)})
	return gravatar_url


class Service(object):
	'''basic service class for all logic classes which needs a db connection.'''
	
	@staticmethod
	def create_conn():
		return create_db_connection() # db.Connection(db_config['dbfile'])

	@property
	def conn(self):
		if not hasattr(self, '_conn') or not self._conn:
			self._conn = Service.create_conn()
		return self._conn
	
	def query(self, *args):
		return self.conn.read(*args)

	def execute(self, *args):
		return self.conn.execute(*args)

	def clear_conn(self):
		if not hasattr(self, '_conn'): return
		if not self._conn: return
		if self._conn:
			try:
				self._conn.close()
				self._conn = None
			except:
				pass

	def __del__(self):
		self.clear_conn()


class DataModel(Service):
	def __new__(cls, *args):
		newobj = super(Service, cls).__new__(cls)
		if hasattr(cls, 'fields'):
			fields = cls.fields
			datadict = args[0] if args else {}
			get_field_value = lambda f: datadict.get(f) 
			map(lambda f:setattr(newobj, f, get_field_value(f)), fields)
		return newobj
	
	def duplicate(self, id_included=False):
		cls = self.__class__
		if hasattr(cls, 'fields'):
			new_obj = cls()
			if id_included:
				fields = [f for f in cls.fields]
			else:
				fields = [f for f in cls.fields if f != 'id']
			for f in fields:
				setattr(new_obj, f, getattr(self, f))
			return new_obj
		return None


class MediaFile(DataModel):
	fields = ('id', 'filename', 'filesize', 'createdate', 'userid', 'postid')

	@property
	def createdate_dt(self):
		return datetime.datetime.fromtimestamp(self.createdate or 0)
	
	def save(self):
		if self.id:
			values = (self.filename, self.filesize or 0, self.createdate or 0, 
				self.userid, self.postid or 0, self.id)
			self.execute('''update t_mediafile (
			set filename=%s, filesize=%s, createdate=%s, 
			userid=%s, postid=%s where id=%s''', *values)
		else:
			values = (self.filename, self.filesize or 0, self.createdate or 0, 
				self.userid, self.postid or 0)
			self.execute('''insert into t_mediafile (
			filename, filesize, createdate, userid, postid) values(
				%s, %s, %s, %s, %s
			)''', *values)
		self.clear_conn()
	
	def delete(self):
		if self.id:
			self.execute('delete from t_mediafile where id=%s', self.id)
			self.clear_conn()

class SidebarLink(DataModel):
	fields = ('id', 'userid', 'listindex', 'label', 'url', 
		'highlight', 'linkdesc', 'position')
	
	@property
	def is_highlight(self):
		return self.highlight > 0
	
	def save(self):
		if self.id:
			values = (self.listindex, self.label, self.url, self.highlight,
				self.linkdesc, self.position, self.id, self.userid)
			self.execute('''update t_link set
				listindex=%s, label=%s, url=%s, highlight=%s, 
				linkdesc=%s, position=%s where id=%s and userid=%s 
			''', *values)
		else:
			values = (self.listindex, self.label, self.url, self.highlight,
				self.linkdesc, self.position, self.userid)
			self.execute('''insert into t_link (listindex, label, url, 
				highlight, linkdesc, position, userid) values(
					%s, %s, %s, %s, %s, %s, %s 
				)''', *values)
		self.clear_conn()
	
	def delete(self):
		if self.id:
			self.execute('delete from t_link where id=%s', self.id)
			self.clear_conn()

class Category(DataModel):
	fields = ('id', 'categoryname', 'userid', 'listindex', 'postcount')
	
	def save(self):
		if self.id:
			values = (self.categoryname, self.listindex, self.postcount,
				self.id, self.userid)
			self.execute('''update t_category set
				categoryname=%s, listindex=%s, postcount=%s 
				where id=%s and userid=%s 
			''', *values)
		else:
			values = (self.categoryname, self.listindex, self.postcount, self.userid)
			self.execute('''insert into t_category (categoryname, 
			listindex, postcount, userid ) values(%s, %s, %s, %s)
			''', *values)
		self.clear_conn()
	
	def delete(self):
		if self.id:
			self.execute('''update t_post set categoryid=0 
				where categoryid=%s and userid=%s''', self.id, self.userid)
			self.execute('delete from t_category where id=%s', self.id)
			self.clear_conn()

class Comment(DataModel):
	''''''
	fields = ('id', 'postid', 'userid', 'parentid',
			'author', 'email', 'sourceurl', 'clientip', 'content',
			'createdate', 'status', 'upvote', 'downvote')
	pt_html_mark = re.compile(r'\<[^\<\>]+\>')
	
	def __init__(self, datadict={}):
		if datadict.has_key('content'):
			htmlmark_pt = self.__class__.pt_html_mark
			self.content = htmlmark_pt.sub('', datadict.get('content') or '')
	
	@property
	def createdate_dt(self):
		return datetime.datetime.fromtimestamp(self.createdate or 0)
	
	@property
	def brief(self):
		if not hasattr(self, '_brief'):
			self._brief = self.content if len(self.content)<15 else \
				u'%s...' % self.content[:15]
		return self._brief
	
	@property
	def icon(self):
		'''use Gravatar.com as the icon service.'''
		return get_icon_url(self.email or self.author)
	
	def delete(self):
		if self.id:
			self.execute('update t_comment set parentid=0 where parentid=%s', self.id)
			self.execute('delete from t_comment where id=%s', self.id)
			self.clear_conn()
	
	def save(self):
		if self.id:
			values = (self.author, self.email, self.sourceurl, self.clientip,
			self.content, self.status, self.upvote, self.downvote, self.id)
			self.execute('''update t_comment set 
			author=%s, email=%s, sourceurl=%s, clientip=%s,
			content=%s, status=%s, upvote=%s, downvote=%s 
			where id=%s''', *values)
		else:
			values = (self.postid, self.userid, self.parentid, 
				self.author, self.email, self.sourceurl, self.clientip,
				self.content, self.createdate, self.status, 
				self.upvote, self.downvote)
			self.execute('''insert into t_comment (
			postid, userid, parentid, author, email, sourceurl,
			clientip, content, createdate, status, upvote, downvote
			) values ( %s, %s, %s, %s, %s, %s, 
			%s, %s, %s, %s, %s, %s
			)''', *values)
			new_rowid = self.conn.lastrowid
			if new_rowid:
				self.id = new_rowid
		self.clear_conn()
	
	def update_votes(self, upvote, downvote):
		if self.id:
			self.upvote = upvote
			self.downvote = downvote
			self.execute('''update t_comment set upvote=%s, downvote=%s 
				where id=%s''', self.upvote, self.downvote, self.id)
			self.clear_conn()

class Post(DataModel):
	fields = ('id', 'subject', 'subjecturl', 'frontphoto', 'content',
			'createdate', 'status', 'userid', 'categoryid', 'commentcount',
			'lastmodified', 'allowcomment', 'highlight', 
			'upvote', 'downvote')
	
	pt_ub = re.compile(r'http\:\/\/untitledblue.com', re.I)
	pt_img_rss = re.compile(
		r'\<img[^\<\>]+?src\=(?:[\'"]?)(?P<src>\/[^\s\'\"]+)(?(1)[\'"]?)',
		re.I|re.S)
	pt_p_or_div = re.compile(r'\<(?:p|div)>(?P<text>.+?)\<\/(?:p|div)\>\s*', 
	re.I|re.S)
	pt_html_mark = re.compile(r'\<[^\<\>]+\>')
	pt_element = re.compile(r'\<[^\<\>]+\/\>\s*')
	pt_fontspan = re.compile(r'\<\/?(font|span|strong|b|u)\s?.*?\>', re.I)
	pt_tbl = re.compile(r'\<table', re.I)
	pt_summerblog = re.compile(r'http\:\/\/bluemask.net\/summer\/blog', re.I)

	def __init__(self, datadict={}):
		if datadict.has_key('namealias'):
			self.userlabel = datadict['namealias']
		else:
			self.userlabel = ''
		content = datadict.get('content')
		if content:
			self.content = self.__class__.pt_ub.sub('http://bluemask.net', 
				self.content)
			self.content = self.__class__.pt_summerblog.sub(
				'http://bluemask.net', 
				self.content)
	
	@property
	def createdate_dt(self):
		return datetime.datetime.fromtimestamp(self.createdate or 0)
	
	@property
	def lastmodified_dt(self):
		return datetime.datetime.fromtimestamp(self.lastmodified or 0)
	
	@property
	def is_draft(self):
		return self.status == 0
	
	@property
	def published(self):
		return self.status == 10
	
	@property
	def has_photos(self):
		if not hasattr(self, '_has_photos'):
			self._has_photos = False
		return self._has_photos
	
	def get_blog(self):
		service = BlogService()
		return service.get_blog(userid=self.userid)
	
	@property
	def categories(self):
		if not hasattr(self, '_categories'):
			category = self.get_blog().get_category(categoryid=self.categoryid) if self.categoryid else None
			if category:
				self._categories = [category]
			else:
				self._categories = []
		return self._categories
	
	@property
	def rss_content(self):
		'''content for rss output'''
		if not hasattr(self, '_rss_content'):
			pt = self.__class__.pt_img_rss
			repl = lambda m: m.group().replace(m.group('src'), 'http://%s%s' % (host_name, m.group('src')))
			result = pt.sub(repl, self.content)
			self._rss_content = result
		return self._rss_content
	
	@property
	def brief(self):
		'''shorten content'''
		if not hasattr(self, '_brief'):
			tbl_pt = self.__class__.pt_tbl
			m = tbl_pt.search(self.content)
			if m or not self.content.startswith('<'):
				# if has tables, remove all html marks
				htmlmark_pt = self.__class__.pt_html_mark
				self._brief = htmlmark_pt.sub('', self.content)
				if len(self._brief) > 300:
					self._brief = u'<p>%s</p><p>...</p>' % self._brief[:300]
				return self._brief
			current_len = 0
			short_texts = []
			pt = self.__class__.pt_p_or_div
			element_pt = self.__class__.pt_element
			search_cursor = 0
			while current_len<500:
				m = pt.search(self.content, search_cursor)
				if m:
					search_cursor = m.end()
					part = m.group()
					text = m.group('text').strip()
					if text and text!='&nbsp;':
						short_texts.append(part)
					current_len += len(part)
					if text.lower().startswith('<img'):
						current_len += 300 # a photo equals 300 chars.
					continue
				m = element_pt.search(self.content, search_cursor)
				if m:
					search_cursor = m.end()
					part = m.group()
					short_texts.append(part)
					current_len += len(part)
					if part.lower().startswith('<img'):
						current_len += 300 # a photo equals 300 chars.
				else:
					break
			if search_cursor < len(self.content)-1:
				short_texts.append('<p class="more">...</p>')
			self._brief = ''.join(short_texts)
			# remove all 'font' marks
			font_pt = self.__class__.pt_fontspan
			self._brief = font_pt.sub('', self._brief) 
		return self._brief
	
	def delete(self):
		if self.id:
			self.execute('delete from t_comment where postid=%s', self.id)
			self.execute('delete from t_post where id=%s', self.id)
			self.clear_conn()

	def save(self):
		if self.id:
			values = (
				self.subject, self.subjecturl, self.frontphoto,
				self.content, self.status, self.categoryid, self.commentcount,
				self.lastmodified, 1 if self.allowcomment else 0,
				1 if self.highlight else 0,
				self.upvote, self.downvote, self.id )
			self.execute('''update t_post set
			subject=%s, subjecturl=%s, frontphoto=%s,
			content=%s, status=%s, categoryid=%s, commentcount=%s,
			lastmodified=%s, allowcomment=%s, highlight=%s,
			upvote=%s, downvote=%s 
			where id=%s
			''', *values)
		else:
			values = (
				self.subject, self.subjecturl, self.frontphoto, self.content, 
				self.createdate, self.status, self.userid, self.categoryid, 
				self.commentcount, self.lastmodified, 1 if self.allowcomment else 0, 
				1 if self.highlight else 0, self.upvote, self.downvote )
			self.execute('''insert into t_post (
			subject, subjecturl, frontphoto, content, createdate,
			status, userid, categoryid, commentcount, lastmodified, 
			allowcomment, highlight, upvote, downvote) values(
			%s, %s, %s, %s, %s,
			%s, %s, %s, %s, %s, 
			%s, %s, %s, %s)''', *values)
			new_rowid = self.conn.lastrowid
			if new_rowid:
				self.id = new_rowid
		self.clear_conn()
	
	def update_votes(self, upvote, downvote):
		if self.id:
			self.upvote = upvote
			self.downvote = downvote
			self.execute('''update t_post set upvote=%s, downvote=%s 
			where id=%s''', self.upvote, self.downvote, self.id)
			self.clear_conn()

	def get_comments(self, page=1, pagesize=16):
		'''return (Comment_items, more), set pagesize to zero to read all comments.'''
		sql_args = [self.id]
		sql = ['select * from t_comment where postid=%s order by id ASC']
		has_more = False
		if pagesize>0 and page>0:
			offset = (page-1) * pagesize
			sql.append(' limit %s, %s')
			sql_args.append(offset)
			sql_args.append(pagesize + 1) 
			results = self.query(''.join(sql), *sql_args)
			if len(results)>pagesize:
				has_more = True
				results = results[:pagesize]
		else:
			results = self.query(''.join(sql), *sql_args)
		self.clear_conn()
		return ([Comment(row) for row in results], has_more)
	
	def recount_comments_count(self):
		if self.id:
			results = self.query('select count(id) as comment_count from t_comment where postid=%s', self.id)
			if results:
				comment_count = results[0]['comment_count']
				self.execute('update t_post set commentcount=%s where id=%s', comment_count, self.id)
			self.clear_conn()


class BlogUser(DataModel):
	fields = ('id', 'username', 'domainname', 
			'namealias', 'email', 'fullname',
			'createdate', 'lastlogindate', 'theme', 'status',
			'isroot')

	@property
	def createdate_dt(self):
		return datetime.datetime.fromtimestamp(self.createdate or 0)
	
	@property
	def lastlogindate_dt(self):
		return datetime.datetime.fromtimestamp(self.lastlogindate or 0)
	
	@property
	def is_root_user(self):
		return self.isroot == 10
	
	@property
	def enabled(self):
		return self.status > 0

	@staticmethod
	def authenticate(username, password):
		'''return (success, BlogUser)'''
		if not username or not password:
			return False,None
		conn = Service.create_conn()
		results = conn.read('''select * from t_user where username=%s ''', username.lower())
		conn.close()
		if results:
			user = results[0]
			if user['passhash'] == password_hash(password):
				userobj = BlogUser(user)
				if userobj.status>0:
					return True, userobj
				else:
					# raise Exception('user disabled.')
					return False, userobj
			else:
				# raise Exception('password incorrect.')
				return False, None
		return False, None 
	
	def change_password(self, new_password, current_password='', skip_current_password_check=False):
		'''return (success, fail_reason_str)'''
		changed = False
		fail_msg = ''
		if not new_password:
			# raise Exception('Empty new password.')
			fail_msg = u'新密码为空。'
		if not skip_current_password_check:
			if not current_password:
				# raise Exception('Current password is required.')
				fail_msg = u'当前密码必须指定。'
		if self.id:
			new_passhash = password_hash(new_password)
			ok_to_change = False
			if not skip_current_password_check:
				results = self.query('select * from t_user where id=%s', self.id)
				if results:
					userdata = results[0]
					if userdata['passhash'] != password_hash(current_password):
						# raise Exception('Incorrect password.')
						fail_msg = u'当前密码不正确。'
					else:
						ok_to_change = True
			else:
				ok_to_change = True
			if ok_to_change:
				self.execute('''update t_user set passhash=%s where id=%s''', new_passhash, self.id)
				changed = True
			self.clear_conn()
		return changed, fail_msg

	def update_settings(self):
		# updatable_fields = ['domainname', 'createdate', 'lastlogindate', 'theme', 'status',	'isroot']
		if self.id:
			self.execute('''update t_user set domainname=%s, createdate=%s, 
			lastlogindate=%s, theme=%s, status=%s, isroot=%s,
			namealias=%s, email=%s, fullname=%s where id=%s''', \
			self.domainname, self.createdate, self.lastlogindate, \
			self.theme, self.status, self.isroot, 
			self.namealias, self.email, self.fullname,
			self.id)
			self.clear_conn()
	
	def update_role(self, isroot=None):
		if self.id and isroot is not None:
			self.isroot = 10 if isroot else 0
			self.execute('''update t_user set isroot=%s where id=%s''', self.isroot, self.id)
			self.clear_conn()
	
	def update_status(self, status=None):
		if self.id and status is not None:
			self.status = status
			self.execute('''update t_user set status=%s where id=%s''', self.status, self.id)
			self.clear_conn()

	def get_blog(self):
		if self.id:
			return Blog(userid=self.id)
		return None


class Blog(Service):
	
	def __init__(self, userid=0, username=''):
		self.userid = userid
		self.username = username

	def get_owner(self):
		if hasattr(self, '_owner'):
			return self._owner
		if self.userid:
			results = self.query('select * from t_user where id=%s', self.userid)
		else:
			results = self.query("select * from t_user where username=%s", self.username)
		self.clear_conn()
		blogowner = BlogUser(results[0]) if results else None
		self._owner = blogowner
		if self._owner:
			self.userid = self._owner.id
			self.username = self._owner.username
		return self._owner

	def exists(self):
		return self.get_owner() is not None

	def delete(self):
		'''delete posts, comments and the owner user of the blog'''
		owner = self.get_owner()
		if not owner:
			return False
		self.execute('delete from t_comment where userid=%s', owner.id)
		self.execute('delete from t_post where userid=%s', owner.id)
		self.execute('delete from t_category where userid=%s', owner.id)
		self.execute('delete from t_user where id=%s', owner.id)
		self.clear_conn()
		self._owner = None
	
	def get_categories(self):
		owner = self.get_owner()
		results = self.query('''select * from t_category where userid=%s 
			order by listindex ASC''', owner.id)
		self.clear_conn()
		if results:
			return [Category(row) for row in results]
		return []
	
	def get_category(self, categoryid):
		owner = self.get_owner()
		results = self.query('select * from t_category where userid=%s and id=%s', 
			owner.id, categoryid)
		self.clear_conn()
		if results:
			return Category(results[0])
		return None
	
	def get_category_by_name(self, categoryname):
		owner = self.get_owner()
		results = self.query('select * from t_category where userid=%s and categoryname=%s', 
			owner.id, categoryname)
		self.clear_conn()
		if results:
			return Category(results[0])
		return None
	
	def get_post(self, postid):
		owner = self.get_owner()
		results = self.query('select * from t_post where id=%s and userid=%s', 
			postid, owner.id)
		self.clear_conn()
		post = Post(results[0]) if results else None
		return post

	def get_posts(self, page=1, pagesize=7, categoryid=0, only_published=False):
		'''return (Post_items, more) '''
		userid = self.get_owner().id if not self.userid else self.userid
		sql_args = [userid]
		sql = ['select p.*, u.namealias from t_post p, t_user u']
		sql.append(' where u.id=p.userid')
		sql.append(' and p.userid=%s ')
		if only_published:
			sql.append(' and p.status=10 ')
		if categoryid>0:
			sql.append(' and p.categoryid=%s ')
			sql_args.append(categoryid)
		sql.append('order by p.id desc')
		has_more = False
		if pagesize>0 and page>0:
			offset = (page-1) * pagesize
			sql.append(' limit %s, %s')
			sql_args.append(offset)
			sql_args.append(pagesize + 1) 
			results = self.query(''.join(sql), *sql_args)
			if len(results)>pagesize:
				has_more = True
				results = results[:pagesize]
		else:
			results = self.query(''.join(sql), *sql_args)
		self.clear_conn()
		return ([Post(row) for row in results], has_more)
	
	def get_comments(self, page=1, pagesize=16):
		'''return (Comment_items, more), set pagesize to zero to read all comments.'''
		userid = self.get_owner().id if not self.userid else self.userid
		sql_args = [userid]
		sql = ['select * from t_comment where userid=%s order by id DESC']
		has_more = False
		if pagesize>0 and page>0:
			offset = (page-1) * pagesize
			sql.append(' limit %s, %s')
			sql_args.append(offset)
			sql_args.append(pagesize + 1)  
			results = self.query(''.join(sql), *sql_args)
			if len(results)>pagesize:
				has_more = True
				results = results[:pagesize]
		else:
			results = self.query(''.join(sql), *sql_args)
		self.clear_conn()
		return ([Comment(row) for row in results], has_more)
	
	def get_recent_comments(self, count=8):
		return self.get_comments(page=1, pagesize=count)
	
	def get_comment(self, commentid):
		userid = self.get_owner().id if not self.userid else self.userid
		results = self.query('select * from t_comment where id=%s and userid=%s',
			commentid, userid)
		self.clear_conn()
		if results:
			commentdata = results[0]
			return Comment(commentdata)
		return None
	
	def get_links(self):
		userid = self.get_owner().id if not self.userid else self.userid
		results = self.query('''select * from t_link where userid=%s 
			order by listindex ASC''', userid)
		self.clear_conn()
		if results:
			return [SidebarLink(row) for row in results]
		return []
	
	def get_link(self, linkid):
		userid = self.get_owner().id if not self.userid else self.userid
		results = self.query('''select * from t_link where userid=%s and id=%s''', 
			userid, linkid)
		self.clear_conn()
		if results:
			return SidebarLink(results[0])
		return None


class BlogService(Service):
	
	def clean_all(self):
		'''!!! WARNING !!! THIS METHOD WILL WIPE OUT ALL DATA! '''
		self.execute('delete from t_comment')
		self.execute('delete from t_mediafile')
		self.execute('delete from t_post')
		self.execute('delete from t_category')
		self.execute('delete from t_user')
		self.clear_conn()
		
	def recount_all_comments_count(self):
		results = self.query('select count(id) as comment_count, postid from t_comment group by postid')
		if results:
			for row in results:
				c_count = row['comment_count']
				postid = row['postid']
				self.execute('update t_post set commentcount=%s where id=%s', c_count, postid)
		self.clear_conn()

	def get_users(self, only_enabled_users=False):
		sql = ['select * from t_user ']
		if only_enabled_users:
			sql.append(' where status>0 ')
		sql.append('order by username ASC')
		results = self.query(''.join(sql))
		self.clear_conn()
		return [BlogUser(row) for row in results]
	
	def get_links(self):
		results = self.query('select * from t_link order by listindex asc')
		self.clear_conn()
		if results:
			return [SidebarLink(row) for row in results]
		return []

	def get_post(self, postid, only_published=False):
		sql = []
		sql.append('select * from t_post where id=%s ')
		if only_published:
			sql.append(' and status=10 ')
		results = self.query(''.join(sql), postid)
		self.clear_conn()
		post = Post(results[0]) if results else None
		return post
	
	def get_posts(self, page=1, pagesize=7, only_published=False, 
		keywords='', order_desc=True):
		'''return (Post_items, more) '''
		sql_args = []
		sql = ['select p.*, u.namealias from t_post p, t_user u']
		sql.append(' where u.id=p.userid ')
		if only_published:
			sql.append(' and p.status=10')
		if keywords:
			sql.append(' and (p.subject like %s) ')
			arg_k = '%s%s%s' % ('%',keywords,'%')
			sql_args.append(arg_k)
			# sql_args.append(arg_k)
		if order_desc:
			sql.append(' order by p.id desc')
		else:
			sql.append(' order by p.id asc')
		has_more = False
		if pagesize>0 and page>0:
			offset = (page-1) * pagesize
			sql.append(' limit %s, %s')
			sql_args.append(offset)
			sql_args.append(pagesize + 1) 
			try:
				results = self.query(''.join(sql), *sql_args)
			except Exception, e:
				print self.conn.lastsql
				results = []
			if len(results)>pagesize:
				has_more = True
				results = results[:pagesize]
		else:
			results = self.query(''.join(sql), *sql_args)
		self.clear_conn()
		return ([Post(row) for row in results], has_more)
	
	def get_comments(self, page=1, pagesize=12, only_published=False, order_desc=True):
		'''return (Comment_items, more), set pagesize to zero to read all comments.'''
		sql_args = []
		sql = ['select c.* from t_comment c, t_post p']
		sql.append(' where c.postid=p.id ')
		if only_published:
			sql.append(' and p.status=10 ')
		if order_desc:
			sql.append('order by c.id DESC')
		else:
			sql.append('order by c.id ASC')
		has_more = False
		if pagesize>0 and page>0:
			offset = (page-1) * pagesize
			sql.append(' limit %s, %s')
			sql_args.append(offset)
			sql_args.append(pagesize + 1)  
			results = self.query(''.join(sql), *sql_args)
			if len(results)>pagesize:
				has_more = True
				results = results[:pagesize]
		else:
			results = self.query(''.join(sql), *sql_args)
		self.clear_conn()
		return ([Comment(row) for row in results], has_more)
	
	def get_blog(self, userid=0, username=''):
		if not userid and not username:
			raise Exception('"userid" or "username" is required.')
		return Blog(userid=userid, username=username)

	def create_blog(self, userinfo={}):
		full_userinfo = {'username':'', 
		'namealias': '', 'email': '', 'fullname':'',
		'passhash':'', 'domainname':'', 'createdate':0, 
		'theme':'', 'status':0, 'isroot':0, 'lastlogindate':0}
		if userinfo:
			full_userinfo.update(userinfo)
		if not full_userinfo['username']:
			raise Exception('Username is required.')
		elif len(full_userinfo['username'])<2:
			raise Exception('Username is invalid.')
		if not full_userinfo['passhash']:
			raise Exception('Hash result of password "passhash" is required.')
		uname_results = self.query("select id from t_user where username=%s", \
			full_userinfo['username'])
		self.clear_conn()
		if uname_results:
			raise Exception('Username already exists.')
		self.execute('''insert into t_user (username, passhash,
			namealias, email, fullname, 
			domainname, createdate, lastlogindate, theme, status, 
			isroot) values (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)''', \
			full_userinfo['username'].lower(), \
			full_userinfo['passhash'], \
			full_userinfo['namealias'], \
			full_userinfo['email'], \
			full_userinfo['fullname'], \
			full_userinfo['domainname'], \
			full_userinfo['createdate'], \
			full_userinfo['lastlogindate'], \
			full_userinfo['theme'], \
			full_userinfo['status'], \
			full_userinfo['isroot'])
		last_rowid = self.conn.lastrowid 
		self.clear_conn()
		return last_rowid


if __name__ == '__main__':
	script_cmd = sys.argv[1] if len(sys.argv)>1 else ''
	handled = False
	if script_cmd:
		if script_cmd == 'syncdb':
			print 'Checking tables...'
			for t in sync_db():
				print t
			print 'Done!'
			handled = True
		elif script_cmd == 'test':
			simple_test()
			handled = True
		if not handled:
			print 'Unknown command.'
	else:
		print '''usage: python blog.py [command]
Command:
    syncdb	: check tables exsitance, create it if not exists.
'''







